﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using NRails;
using NRails.Database;
using NRails.Database.Schema;
using NRails.Migrations;
using NRails.Macros.Migration;
using System.Data;

namespace NRails.Tests
{
  public class MigrationTests: AssertionHelper
  {
      class CreateMigrationTest : Migration
      {
          public this () {base("1")}
          
          protected override Up() : void
          {
              create Test
              {
                  Id : Guid(pk);
                  Zip : string?(len = 10) = "zzzz";
                  createindex IDX_Test(Id(desc), Zip);
              };
              
              change Test
              {
                  Street : string;
                  change Zip : string(len = 20);
                  rename Zip Zap;
                  drop Id;
                  dropindex IDX_Test;
              }
              drop Test;
          }
      }

      [Test]
      public CreateTableTest() : void
      {
          CreateTableTest.[CreateMigrationTest](actions =>
          {
              match (actions)
              {
                | [MigrationAction.CreateTable(table, tableActions), 
                   MigrationAction.ChangeTable(table2, table2Actions),
                   MigrationAction.DropTable(table3)] => 
                    Expect(table.Name, Is.EqualTo("Test"));
                    match (tableActions)
                    {
                        | [TableAction.AddColumn(col), 
                           TableAction.AddColumn(col2),
                           TableAction.CreateIndex(idx)] => 
                            Expect(col.Name, Is.EqualTo("Id"));
                            Expect(col.Type.DbType, Is.EqualTo(SqlDbType.UniqueIdentifier));
                            
                            Expect(col2.Name, Is.EqualTo("Zip"));
                            Expect(col2.Type.DbType, Is.EqualTo(SqlDbType.NVarChar));
                            Expect(col2.Size, Is.EqualTo(10));
                            Expect(col2.Nullable, Is.EqualTo(true));
                            Expect(col2.DefaultValue, Is.EqualTo("'zzzz'"));
                            Expect(idx.Name, Is.EqualTo("IDX_Test"));
                            Expect(idx.Columns, Is.EqualTo("Id DESC, Zip"));
                            
                        | _ => Assert.Fail("2 AddColumn actions expected");
                    }
                    match (table.Keys.ToNList())
                    {
                        | [key] =>
                          Expect(key.Columns, Is.EqualTo("Id"));
                          Expect(key.KeyType, Is.EqualTo(ConstraintType.KeyPrimary));
                        | _ => Assert.Fail("pk")
                    }
                    Expect(table2.Name, Is.EqualTo("Test"));
                    match (table2Actions)
                    {
                        | [TableAction.AddColumn(col), 
                           TableAction.ChangeColumn(colZip, colNewZip),
                           TableAction.RenameColumn(renamedColumn, oldName, newName),
                           TableAction.DropColumn(colId),
                           TableAction.DropIndex(idx)] =>
                           def cols = [col, colZip, colNewZip, colId, renamedColumn];
                           Expect(cols.Map(_.Name), Is.EquivalentTo(["Street", "Zip", "Zip", "Id", "Zip"]));
                           Expect(cols.Map(c => c.Type.DbType), Is.EquivalentTo([SqlDbType.NVarChar, SqlDbType.NVarChar, SqlDbType.NVarChar, SqlDbType.UniqueIdentifier, SqlDbType.NVarChar]));
                           Expect(colNewZip.Nullable, EqualTo(false));
                           Expect(colNewZip.Size, EqualTo(20));
                           Expect((oldName, newName), EqualTo(("Zip", "Zap")));
                           
                           Expect(idx.Name, Is.EqualTo("IDX_Test"));
                        | _ => Assert.Fail("change");
                    }
                    Expect(table3.Name, EqualTo("Test"));
                | _ => Assert.Fail();
              }
          });
      }
      
      protected CreateTableTest[T](checkActions : list[MigrationAction] -> void) : void where T : Migration
      {
          def schema = DBSchema();
          def migr = CreateMigrationTest();
          
          def migrService = migr : IMigration;
          migrService.AssignSchema(schema, Engine.Instance.CreateDbManager());
          
          migrService.Up();
          
          checkActions(migrService.GetActions());
      }
  }
}
